package Biblioteca;

import java.io.File;
import java.io.IOException;
import GUI.BibliotecaWindow;
import java.net.URLEncoder;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Clase que implementa un parser con SAX para construír un objeto biblioteca desde un xml.
 */
public class LectorSAX {

    Biblioteca biblioteca;
    BibliotecaWindow ventana;

    /**
     * Devuelve la biblioteca si se ha cargado, null en caso contrario.
     * @return Biblioteca o null
     */
    public Biblioteca getBiblioteca() {
        return this.biblioteca;
    }

    /**
     * Constructora de la clase. Abre el archivo xml cuyo path es ruta y lo parsea, construyendo
     * una biblioteca. Si no existe, la biblioteca es null.
     * @param ruta Ruta del archivo xml
     * @param ventana Ventana padre.
     */
    public LectorSAX(String ruta, BibliotecaWindow ventana) {
        try {
            File f = new File(ruta);
            this.ventana = ventana;
            if (f.exists()) {
                SAXParserFactory spf = SAXParserFactory.newInstance();
                SAXParser sp = spf.newSAXParser();
                sp.parse(f, new LibraryXMLReader());

            } else {
                this.biblioteca = null;
            }
        } catch (ParserConfigurationException e) {
            System.err.println("error de  parseo");
            this.biblioteca = null;
        } catch (SAXException e2) {
            System.err.println("error de  sax : " + e2.getStackTrace());
            this.biblioteca = null;
        } catch (IOException e3) {
            System.err.println("error de  io : " + e3.getMessage());
            this.biblioteca = null;
        }

    }

    /**
     * Clase privada que implementa el manejador de eventos de SAX.
     */
    private class LibraryXMLReader extends DefaultHandler {

        private String contenido = "";
        private Genero genero;
        private Artista artista;
        private Album album;
        private Cancion cancion;
        int modo;
        /**
         * Cadena para separar artista de álbum en las claves de esta tabla hash.
         */
        final String cadenaEspecial = "/()/";

        /**
         * Esta función es llamada al comienzo del documento.
         * @throws SAXException
         */
        @Override
        public void startDocument() throws SAXException {
            biblioteca = new Biblioteca("./config/Generos.txt");
            modo = 0;
        }

        /**
         * Esta función es llamada al finalizar el documento.
         * @throws SAXException
         */
        @Override
        public void endDocument() throws SAXException {
            ventana.getBarraProgreso().setEnabled(false);
            ventana.getBarraProgreso().setValue(0);

        }

        /**
         * Esta funcion el llamada cuando se produce el evento  de ver una nueva etiqueta
         * @param uri
         * @param localName
         * @param qName
         * @param attributes
         */
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) {
            if ("artistas".equals(qName)) {
                ventana.getBarraProgreso().setIndeterminate(false);
                ventana.getBarraProgreso().setMaximum(Integer.parseInt(attributes.getValue("numcanciones")));
                ventana.getBarraProgreso().setValue(0);
                ventana.getBarraProgreso().setEnabled(true);
            } else if ("artista".equals(qName)) {
                //Crear artista
                artista = new Artista(decode(attributes.getValue("nombre")));
            } else if ("album".equals(qName)) {
                //Crear album
                album = new Album(decode(attributes.getValue("nombre")));
            } else if ("cancion".equals(qName)) {
                //Crear canción
                cancion = new Cancion(decode(attributes.getValue("path")));
            } else if ("titulo".equals(qName)) {
                //Nada
            } else if ("codec".equals(qName)) {
                //Nada
            } else if ("canales".equals(qName)) {
                //Nada
            } else if ("kbps".equals(qName)) {
                //Nada
            } else if ("longitud".equals(qName)) {
                //Nada
            } else if ("pista".equals(qName)) {
                //Nada
            } else if ("año".equals(qName)) {
                //Nada
            }


        }

        public String decode(String in) {
            String out;
            out = in.replace("-8a31-", "&");
            out = out.replace("-8b31-", "<");
            out = out.replace("-8c31-", ">");
            out = out.replace("-8d31-", "\"");
            out = out.replace("-8e31-", "'");
            return out;
        }

        /**
         * Esta funcion es llamada cuando ve el contenido de una etiqueta
         * @param buf
         * @param offset
         * @param len
         * @throws SAXException
         */
        @Override
        public void characters(char buf[], int offset, int len) throws SAXException {
            contenido = new String(buf, offset, len);
        }

        /**
         * Esta función es llamada al llegar al final de un elemento.
         * @param uri
         * @param localName
         * @param qName
         */
        @Override
        public void endElement(String uri, String localName, String qName) {
            if ("generos".equals(qName)) {
                modo = 1;
            } else if ("genero".equals(qName)) {
                if (modo == 0) {
                    //Lista de géneros.
                    genero = new Genero(contenido);
                    biblioteca.getIndGeneros().add(genero.getNombre());
                    biblioteca.getGeneros().put(genero.getNombre(), genero);
                    genero = null;
                } else if (modo == 1) {
                    //Género de la canción.
                    cancion.setGenero(biblioteca.getGenero(contenido));
                    biblioteca.getGenero(contenido).addCancion(cancion);
                }

            } else if ("artista".equals(qName)) {
                //Añadir artista a la biblioteca
                biblioteca.getIndArtistas().add(artista.nombre);
                biblioteca.getArtistas().put(artista.getNombre(), artista);
                artista = null;
            } else if ("album".equals(qName)) {
                //Añadir álbum al artista y a la biblioteca
                artista.getAlbumes().add(album);
                biblioteca.getIndAlbumes().add(artista.getNombre() + cadenaEspecial + album.getNombre());
                biblioteca.getAlbumes().put(artista.getNombre() + cadenaEspecial + album.getNombre(), album);
                album = null;
            } else if ("cancion".equals(qName)) {
                cancion.setArtista(artista);
                cancion.setAlbum(album);
                //Añadir canción al artista, al álbum y a la biblioteca, y al género.
                artista.getCanciones().add(cancion);
                album.getCanciones().add(cancion);
                biblioteca.getIndCanciones().add(cancion.getRuta());
                biblioteca.getCanciones().put(cancion.getRuta(), cancion);
                cancion = null;
                ventana.getBarraProgreso().setValue(ventana.getBarraProgreso().getValue() + 1);
            } else if ("titulo".equals(qName)) {
                //Poner título en canción
                cancion.setTitulo(contenido);
                contenido = "";
            } else if ("codec".equals(qName)) {
                //Poner códec en canción
                cancion.setTag("codec", contenido);
                contenido = "";
            } else if ("canales".equals(qName)) {
                //Poner canales en canción
                cancion.setTag("canales", contenido);
                contenido = "";
            } else if ("bitrate".equals(qName)) {
                //Poner biltrate en canción
                cancion.setTag("bitrate", contenido);
                contenido = "";
            } else if ("duracion".equals(qName)) {
                //Poner duración en canción
                cancion.setTag("duracion", contenido);
                contenido = "";
            } else if ("pista".equals(qName)) {
                //Poner pista en canción
                cancion.setTag("pista", contenido);
                contenido = "";
            } else if ("anio".equals(qName)) {
                //Poner año en canción.
                cancion.setTag("año", contenido);
                contenido = "";
            }
        }
    }
}
